home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Gold Medal Software 2
/
Gold Medal Software Volume 2 (Gold Medal) (1994).iso
/
prog
/
cmagic.arj
/
TUTOR.DOC
< prev
Wrap
Text File
|
1992-09-06
|
61KB
|
1,256 lines
C MAGIC
Version 1.5
(c) Copyright 1992, Jeff Napier & Another Company
Tutorial File
What C Magic can do for you:
This disk consists of three main parts, MAGIC.LIB,
MAGIC.TXT and TUTOR.DOC. MAGIC.LIB is an add-in for
Borland's Turbo C and BORLAND C products. It will work on
versions from Turbo C 1.5 through Borland C++ version 3.1,
and probably work on future versions as well.
C MAGIC which will make your job as a programmer much
easier and your results much more polished. MAGIC.TXT is a
text file which explains how to use all the features of
MAGIC.LIB, and TUTOR.DOC, the file you are now reading, is a
tutorial which starts at a very beginning level and quickly,
but effortlessly moves into advanced programming techniques.
Specifically, MAGIC.LIB offers the following:
* Automatic pop-up text boxes, dialog boxes and light-bar
menus.
* Automatic handling of text files.
* Built-in mouse and cursor support for the programs you
create.
* Support for graphics programs to make them as easy to write
as text-based programs. This includes a universal mouse
cursor which will operate from the keyboard as well as from
mouse movement whether or not a mouse is available.
* Incorporation of .BGI and .CHR files directly into finished
.EXE files.
* Built-in 256-color VGA support. (Except with older versions
of Turbo C.)
* Sound effects for your programs.
* Simplification of many standard C operations.
* Complete color control in text and graphics modes.
* Full, documented, easy-to-understand source code is
available so you can learn from and customize the Magic
framework to your own applications.
* OOP supported but not required.
NOTE:
This is a shareware product. You have our permission to
copy and distribute this package as long as all files remain
intact and unchanged.
If you benefit from this tutorial, or if you use the
MAGIC.LIB unit in your programs you are expected to register.
To register, send $39.95 to:
Another Company
P.O. Box 298
Applegate, OR 97530
USA
Please add $3 per order for postage in the USA, $5 for
postage to Canada, or $7 for postage to all other countries.
Please add $1 if you require 3.5" disk size.
If you would like the full source code sent with your
registration, send $79.90 plus postage. Please add $2 if you
require 3.5" disk size.
QUESTIONS:
If you have questions: 503-846-7884, 9-5 Weekdays, West Coast
time. (We are not always available during those hours, but
you can try.)
THE BEST WAY TO USE THIS TUTORIAL:
Copy MAGIC.LIB to the directory where you keep your .LIB
files, copy MAGIC.H to the directory which contains your
include (*.H) files, and copy TUTOR.DOC to the directory
which contains your source code files. Start your BC.EXE or
TC.EXE and load in this file (type: BC TUTOR.DOC). Since the
IDE* (Integrated Development Environment) allows you to work
on several files at once, you can read this tutorial in one
window and work on practice programs in another window. If
you don't know how yet, I'll show you. If you are an
experienced programmer, you might want to skim through this
tutorial and concentrate only on the areas that are new to
you.
* Some of the older versions of Turbo C do not have a
multi-window IDE. With these versions, simply switch from
one file to another as required.
A COUPLE OF OTHER NOTES:
1. It is assumed that you have legally purchased a Borland C
compiler and therefore have the valuable printed manuals that
come with the kit. If not, you are advised to spend a little
money and get what you need, because there are places within
this tutorial which will refer you to those books to complete
your knowledge.
2. This is not the last word on C. It doesn't teach
everything about programming - probably not even 10 percent
of what you will eventually learn, but between this tutorial
and the Borland manuals, you'll get pretty good at
programming, and fairly quickly. One of the best ways to
learn this is to experiment frequently. Tear the example
programs apart and try to interject your own ideas into them.
See what you can change or improve, then run the programs you
have modified and see what happens. As you well know, you
can't hurt your computer by typing an incorrect line of code.
3. C is sensitive to capitalization. You must be careful to
use capital letters and lower case letters in the right
places. The exact placement of indentations is not important,
however, and every programmer develops a slightly different
style.
4. This is version 1.5 of what I believe is a good product,
but then, I wrote it. Let me know what you think. I'm pretty
sure I caught all the major bugs, but there may be some minor
ones. Most likely the registered and/or subsequent shareware
versions will be even better! Registered users always get
the very latest version with whatever improvements have been
added since the shareware release.
HERE WE GO:
Here is the simplest program you can write:
main() {}
Let's try it out. Press [F3]. (First finish reading this
paragraph so you know how to get back.) A window pops up
asking for a file name. Call it Prog1 (or any name you
choose) and type the preceding one-line program. Press [F2]
to save your valuable work against power failure, etc. While
holding the [Ctrl] key, press [F9] and the program will run.
(With older versions of Turbo C, hold [Alt] and press [r].)
Did you blink? Since, the program doesn't actually do
anything, it runs very quickly, then returns to the IDE.
IDE stands for Integrated Development Environment, what
Borland calls their compiler. We'll use the same name rather
than trying to say Borland C, or something equally confusing.
Let's look at the program you just ran in detail. Main()
is a function. Every C program must have one main() function.
This program was a very simple one, and so there are no
additional details needed between the parentheses. They are
there simply to tell the computer that "main" is a function,
not a number or an instruction of some sort. The main()
function starts after the curly bracket. It is there to tell
the computer that this is where the function will begin. The
closing curly bracket tells the computer that the function is
done. Since there is nothing between the curly brackets, the
program does nothing.
If you are sharp, you may have noticed that the IDE issued
a warning message. If you don't see it at the bottom of the
screen, press [F6]. Technically, all functions give a
response when they are run unless they are provided with the
special word "void."
So in the next program, we'll precede main() with void.
Next, we'll add something to the program so it will actually
have a purpose:
void main(){
printf("Hello, World!");
}
Go back to your program (hold [Alt] and press [1] or [2]),
and modify it to imitate the above example. Hold [Ctrl] and
press [F9] (or [Alt] + [r]) to run it.
What happened? Well, it worked alright, but if you have
a recent compiler, you did not see anything, because it
happened very quickly and returned to the IDE when it was
done. Let's add another function to slow it down. Getch()
waits patiently for the user to press a key before the
program continues, thereby giving you a chance to see "Hello,
World!" written on the screen. Test this one out:
void main(){
printf("Hello, World!");
getch();
}
If you have a recent Borland compiler, it issued two
warnings! You see, the IDE doesn't trust you to use printf()
or getch() properly. And that's a good thing, because when
you start writing complicated programs, you'll need all the
help you can get from the IDE. Any function that you try to
use is checked to see that you are feeding it right
information. In this case, printf() is a function which
writes text onto the screen. You want printf() to write a
literal string, "Hello, World!", but what if you accidentally
told it to write 14 cucumbers. It wouldn't know what
cucumbers are, or what to do with 14 of them. So C restricts
you to passing information to functions that they can handle.
To do this, you must list the functions that you are going to
use and what kind of information they can handle, before you
use them. A typical C program has a list of function
"prototypes" and their acceptable "parameters" at the top of
the program.
Printf() and getch() are two of hundreds of functions
provided with C in a separate disk file called a library.
There are several such libraries available so you don't have
to reinvent the wheel when you make your own programs. There
are functions for dividing numbers, drawing circles, writing
disk files, and so on.
As a program grows in size, you may end up using a
hundred or so of these functions. Rather than list them all
at the top of your source code, you can "include" a header
file. Borland provides several header files for the
functions in their library files. The one which contains the
prototypes for printf() and getch() is STDIO.H in older
versions of Turbo C, or CONIO.H in new versions. Look at the
following program listing to see how it is included:
#include <stdio.h>
void main(){
printf("Hello, World!");
getch();
}
While you are at it, look at STDIO.H or CONIO.H and look
at the prototypes in the header file.
Again, you can type in the changes and test this
program. Or, you could save yourself a bit of work. If you
have a mouse and a new version compiler:
Move the cursor to the top of the program in this file.
(Move it to the # in "#include." Then select the whole
program by holding down the left mouse button and moving the
mouse until the whole four lines change color. Then select
Edit from the top menu and Copy. Then open a new window
(press [F3]) and then select Paste from the Edit menu.
Or from the keyboard: Move the cursor to the top of the
program and hold [Ctrl] and press [k] then [b]. (This is
referred to as ^kb.) Then, using the arrow keys move to the
bottom of the program listing and press ^kk. Then [Alt] +
[e],[c]. Open a new window (press [F3]) and then select
[Alt] + [e] + [p].
The program will be pasted in the new window. Save the
program with a press of the [F2] key, then run it. (Hold
[Ctrl] and press [F9])
With the older compilers, you use ^kb and ^kk to mark a
block, and then press ^kw to write a block. You'll be asked
to name a file for the block so choose a name like "TEMP".
A file called TEMP.C appears on your disk. Then move into
another file, such as TEST.C, and press ^kr to "read" the
block, TEMP.C into the file.
If you haven't changed the layout of directories and
files, the program will run just fine. If it can't find
STDIO.H, select Options from the top menu, then Directories,
then tell the IDE where it should look for "INCLUDE" files.
Our little program works, but it doesn't look like much,
does it? Here's a wonderful opportunity for me to show you
how much better C Magic is. Let's try a variation of that
program. But first, you'll need to do a couple of things,
Copy MAGIC.LIB into the directory containing your *.LIB
files, and MAGIC.H into the directory containing your other
*.H files (general called the INCLUDE directory).
In order to incorporate MAGIC.LIB into your programs,
you'll need to start a "project". This is easier than it
sounds. Simply choose Project from the top menu, then choose
Open. Give it any DOS-legal name (up to 8 letters) which
will end with .PRJ. Then Choose Include from the Project
menu and type your program name, then MAGIC.LIB, and also
GRAPHICS.LIB. Now all the functions within MAGIC.LIB will be
available to your program.
If you have an older version compiler, you make a .PRJ
file by simply pressing [F3] to edit a new file, and name it
something specifically ending with .PRJ. In this file, you
type your program name (for instance, TEST.C), and then, on
the next line, MAGIC.LIB, and on a third line, GRAPHICS.LIB.
IMPORTANT NOTE:
GRAPHICS.LIB is a library of graphics routines, some of
which are used by MAGIC.LIB, even for non-graphics programs.
Therefore, GRAPHICS.LIB must always be included along with
MAGIC.LIB in your "projects."
And now type or paste this into a file of it's own:
#include <magic.h>
void main(){
xclear();
pile("Hello, World!");
getanykey(10,10);
}
This program should run, and look quite impressive
compared to the earlier version.
If You Have Problems:
Select OPTIONS from the top menu, then COMPILER, then
CODE GENERATION. The memory model MUST be LARGE. Magic will
not work with smaller memory models.
When you start a .PRJ file, the settings you may have
selected from the OPTIONS menu may change. Especially check
the Options | Directories menu.
Another potential problem is that you can forget to use
the graphics library, which must be linked with MAGIC even if
you aren't using any graphics. This is because MAGIC is
always READY for graphics. With Borland C++ version 3.0 or
3.1, select OPTIONS, then LINKER, then LIBRARIES, and make
sure the box for GRAPHICS.LIB is checked. You can leave it
checked for all programs you write, because it is only linked
into programs which use it. The only difference is your
programs will compile a bit slower. With the older versions,
simply include the line GRAPHICS.LIB in your .PRJ files.
While the above program is similar to the previous ones,
you'll see some different functions.
Since the above program uses functions from within
MAGIC.LIB, we use the MAGIC.H header file to prototype the
functions.
The first one, xclear(), simply clears the previous junk
off the screen. Then pile() gets the "Hello, World!" string.
We'll talk more about pile() later. Finally, getanykey()
places the "Hello, World!" string on the screen, but in a
fancy box!
Getanykey() gets two numbers, the coordinates at which
the upper left corner of the pop-up box is to appear. The
first number is the horizontal position. 1 is the extreme
left edge of the screen and 80 is the right-most edge. The
second number is the vertical position which can range from 1
to almost 25. If you use a negative integer for one or both
numbers, the box is automatically centered on the screen,
horizontally, vertically, or both. Like getch(), getanykey()
waits for the user to press any key. Unlike getch(), it then
erases the text box from the screen. Also, it will respond
to a mouse button. If the left button is clicked,
getanykey() acts as if [Enter] was pressed and the right
mouse button is equivalent to [Esc].
Lets add a few things to the previous program:
/* program #6 */
#include <magic.h>
void main(){
magic_setup();
xclear();
pile("Do you want to quit? (Y/N)");
pile("");
pile("This is just an example. Actually, the");
pile("program will quit whether you type Y or N");
getyn(-1,-1);
}
The very top line, starting with /* and ending with */ is
a comment. Comments have no effect on the program, and are
available just for making notes. You can include comments
just about anywhere, but there are two restrictions: You
must have the /* marker at the beginning and the */ at the
end; and you cannot usually include comments within comments.
(Compilers which support C++ also accept // as the start of a
comment and the end of a line as the end.)
Using lots of comments is a good idea because they can
help you think out a program that is giving you trouble. By
adding comments, you can sometimes understand your code
better. Also, they help you remember what you did last week
or last year. They do not change the size of or slow down
the finished .EXE file.
We added a new function from the MAGIC library, this
one, magic_setup(), seemingly does nothing. In fact, it does
lots of stuff in the background, ranging from fixing up a
sound effects error in older compilers to getting ready for
graphics screens should you decide to use graphics. It is
advisable, in fact necessary in most cases, to call
magic_setup() once at the beginning of most programs which
use the MAGIC library.
You can see now that pile() builds a pile of strings of
text for display. You can call pile() over and over again.
Each time you do, another line is added to the "pile." The
pile is actually a global array of 46 strings, called sent[],
each up to 80 characters long.
In this program, getanykey() has been replaced with
getyn(), which waits until the user types [Y], [y], [N], [n],
or clicks the left or right mouse button. (NOTE: The mouse
is optional. It does not have to be hooked up.) When getyn()
is done, it sets a global char variable called "u" to either
capital "Y" or "N".
Also, the coordinates have changed. Getyn() now uses two
negative integers, and will therefore center itself on the
screen.
C mostly isn't sensitive to white space. You can organize
the actual layout of your program differently. It is usually
done with indentations similar to the examples you have been
looking at. This makes the programs much easier to read and
understand. To illustrate this point, look at program #6a,
below, which compiles and runs just like program #6. You'll
notice, however, that the "#include" is on its own line.
Directives starting with # must be on their own line and the
line must start with the #.
/* program #6a */
#include <magic.h>
magic_setup(); void main(){xclear();
pile("Do you want to quit? (Y/N)"); pile("");
pile("This is just an example. Actually, the");
pile("program will quit whether you type Y or N");
getyn(-1,-1);}
In the next program, I'll show how you'd actually use
getyn().
/* program #7 */
#include <magic.h>
void main(){
magic_setup();
xclear();
do {
pile("Do you want to quit? (Y/N)");
pile("");
pile("This version works like it should.");
getyn(-1,-1);
}
while (u != 'Y');
}
Most of the program is the same. But look at the "do
{" line. This starts a loop which executes over and over
again until the condition following the "while" allows the
program to "fall through" or continue past the loop. The
curly bracket following the "do" and on the line before the
"while" contain the statements which are allowed to repeat.
"!=" is C talk for "not equal to." Remember that getyn()
allows only [y], [Y], [n], or [N], or a mouse button, and
returns the variable "u" only as (capital) [Y] or [N]. So,
until the user answers "yes" with [Y] or [y] or the left
mouse button, the computer is gonna keep asking "Do you want
to quit," etc.
You'll notice that the pile()s are repeated as well. This
is because as soon as getyn() is done, it resets all the
strings in the pile to blanks, in preparation for whatever
the programmer might want next. For this loop to work right,
you have to keep forcing the pile back to the strings you
want displayed.
In this next variation, there are two small changes to
pretty it up:
/* program #8 */
#include <magic.h>
void main(){
magic_setup();
xclear();
centerjustify = 1;
musicon = 0;
do {
pile("Do you want to quit? (Y/N)");
pile("");
pile("This version works like it should.");
getyn(-1,-1);
}
while (u != 'Y');
}
First we have set a global variable called centerjustify
to 1. When this is set, all the MAGIC functions which print
the pile to the screen automatically center the strings of
text within the pop-up box. If centerjustify were set to 0,
the default condition, the strings are not centered.
Then, another variable called musicon, which is 1 by
default, has been set to 0, and now sound effects will not
play when pop-up boxes appear and disappear. Using 1 and 0 is
common practice in C programs. 1 is used to represent TRUE
or ON or YES and 0 equals NO or OFF or FALSE.
Compile and run program 8 and see if you like it better.
Now, here's a little project: See if you can add another
loop to program #8 which asks the user "Are you sure?" when
"Do you want to quit?" is answered affirmatively.
You might want to experiment a bit with this program,
entering different messages, and different numbers of lines
of text, and even lines of text of differing lengths.
IMPORTANT NOTE:
NOTE: There is a limit to the size of pop-up boxes. This
limit depends on which graphics mode you are using. For
instance, in VGA high-resolution, if you try to pop up a box
containing more than about 20 lines of text, with the longest
line being perhaps 40 characters long, you'll probably run
out of RAM space. The result of this is a crash.
This next program introduces another Magic function,
dialog(), which also pops a text box onto the screen, but
this box has a blank line at the bottom in which the user
types a response. The answer is returned in a global variable
called "answer."
/* program #9 */
#include <magic.h>
void main(){
magic_setup();
xclear();
pile("How would you answer this question?");
dialog(-1,4);
pile("User typed:");
pile(answer);
getanykey(-1,12);
}
Again, a good way to study this little program is to copy
it into it's own window, try to guess what it does, then run
it and see if it did what you expected.
Notice the two lines "pile()ed" up for getanykey(). The
first has a literal string surrounded by quotation marks.
Nothing new about that. But the next call to pile() passes
the variable, answer, not in quotation marks. If it were in
quotation marks, the program would say:
User typed:
answer
What you want instead is the value answer represents,
which in this case is the string returned by dialog(). Answer
is a global variable that always represents a string of zero
or more characters.
In program #9, you could make the whole operation more
evident if you used some color control. Try program #10.
/* program #10 */
#include <magic.h>
#include <conio.h>
void main(){
magic_setup();
xclear();
pile("How would you answer this question?");
dialog(-1,4);
boxback = WHITE;
border = LIGHTBLUE;
boxtext = BLACK;
shadow = MAGENTA;
mainback = RED;
xclear();
pile("User typed:");
pile(answer);
getanykey(-1,12);
}
Program 10 is ugly, but it shows you how to change the
colors within the text box. The colors seem to be used here
as some sort of variables and they are. They are defined in
the CONIO.H file. (Use GRAPHICS.H if using older versions of
Turbo C.) Boxback, boxtext, shadow, border, and mainback are
global variables from Magic. These variables accept a number
between 0 and 15. CONIO.H assigns BLACK to 0, BLUE to 1, and
so on.
If the program didn't work for you, make sure that you
have "#include"d CONIO.H or GRAPHICS.H and that you have
capitalized the color names, and used small letters for
mainback, boxtext, etc..
Mainback is the main background color, and will not
change unless you call xclear() first to clear the screen and
change the background color. There is another variable
called maintext, for use when writing directly to the screen.
Also available are bartext and barback for the highlight bar
in menu(). (more about menu later)
The colors could just as easily have been represented by
numbers, as in the following example:
/* program #11 */
#include <magic.h>
void main(){
magic_setup();
xclear();
pile("How would you answer this question?");
dialog(-1,4);
boxback = 15;
border = 9;
boxtext = 0;
shadow = 5;
mainback = 4;
xclear();
pile("User typed:");
pile(answer);
getanykey(-1,12);
}
Notice that CONIO.H is not used in this program #11. It
was only used to provide the defined colors which have been
replaced with their normal numbers.
With a bit of experimentation, you will soon discover
that not all 16 colors are available in all situations. Only
the first 8 are available for backgrounds, with the brighter
colors reserved for text components. (In graphics modes, all
16 are available in all situations.)
Let's find out what all the colors are: Here is a slightly
more complex program than any we have made so far:
/* program #12 */
#include <magic.h>
#include <conio.h>
#include <stdlib.h>
void main(){
magic_setup();
mainback = BLACK;
xclear();
border = WHITE;
boxback = BLACK;
centerjustify = 1;
pile("Type a number from 0 to 15.");
dialog(-1,-1);
do {
boxtext = atoi(answer);
if ((boxtext < 1) || (boxtext > 15)) boxtext = 15;
pile("The current color is");
pile(answer);
pile("Type a number from 0 to 15");
pile("or press [Esc] to exit.");
dialog(-1,-1);
}
while (u != 27);
}
Program 12 does this: First, the mainback, border color and
the boxback color are changed and the screen is cleared.
Because centerjustify is set to 1, it will cause text to be
centered in the pop up boxes.
Then a one-line dialog box appears. As soon as the user
answers the question in this dialog box, a loop starts. The
repeat loop changes the boxtext color to the corresponding to
what the user typed in the dialog box. Since answer contains
the ASCII (text) representation of what the user typed, it
must be converted to the actual integer. This is done with
atoi(). Atoi is prototyped in STDLIB.H, and this file is
included at the top of the program.
Since the only 'seeable' colors range from 1 to 15 (#0
is BLACK), the line starting with 'if' repairs the boxtext
number if it not suitable. Notice that it is a compound if.
The || means "or." First, the whole expression evaluated by
if is in parentheses, then each the expression on either side
of the || is also in parentheses. At first, writing sensible
statements containing || (or &&, meaning "and") can be
difficult or confusing, but with practice, it all makes
sense.
Within the loop a new box pops up containing three lines
of text written in the color corresponding to the number the
user typed in the dialog box.
This all repeats until the user presses [Esc], which, as
you probably know is ASCII 27. You see, dialog() builds a
string called answer. It uses the global variable u, a
single character variable, to pick up each key the user
presses, and this is why u is checked for the value of #27.
Program 13 will be our first program that does something
useful in the real world. It will produce the square of an
integer. I know that's not much. You paid around $1000 for
your computer, but your $5 calculator can do squares, and it
can even do it with floating point numbers, which this
example can't. The important thing is, once you learn this,
you can build on this program.
/* program #13 */
#include <magic.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void main(){
int thing;
char tempstr[80];
magic_setup();
xclear();
do {
pile("Type a number to be squared");
pile("or press [Esc] to end program");
dialog(-1,-1);
if (u != 27) {
thing = atoi(answer);
thing *= thing;
sprintf(answer,"%d",thing);
strcpy(tempstr,"The square is ");
strcat(tempstr,answer);
pile(tempstr);
getanykey(-1,-1);
}
}
while (u != 27);
}
First, you'll notice we've included four header files.
Each one contains prototypes for something in this program.
Look at the line below main(). It merely declares "thing" a
variable in which we can store an integer. Because thing
is declared at the start of the main() function, it will be
accessible throughout the program. Variables declared at the
start of the main program are called "global." An "int" can
hold a number between -32,768 and +32,767. If you are sharp,
you can see a flaw in this program already. If the user types
a number greater than 181, resulting in a square greater than
32,767, the program will not work properly. C offers many
ways around this problem. For instance, instead of "int" we
could use "long," a much bigger variable.
The line below that declares "tempstr." This one is an
array variable which can hold up to 80 characters - otherwise
known as a "string." Allowing tempstr to be 80 characters
long is overdoing it a bit, we could have used maybe 20 or
so. In a very large program, you always want to keep your
global variables small, but in a small program, it does not
matter much.
Then we have a typical do - while loop which repeatedly
asks a question until the user presses [Esc] (ASCII 27). If
the user does press [Esc] the block following the "if" does
not execute. Otherwise, the string, answer, is converted to
the integer, thing. Thing is squared, and the function()
sprintf changes the new value of thing back into a string and
the answer is displayed on the screen.
Look carefully at the line: thing *= thing; If it doesn't
make sense to you, that's because it is C shorthand. The same
thing could be expressed as: thing = thing * thing. This
shorthand can also be used with addition (thing += thing),
subtraction and some other mathematical operations.
There are a couple of new string handling techniques
here. Sprintf() is a neat gadget which makes numbers of all
sorts into strings, but requires some reading to understand
fully. (look up sprintf() and printf() in your Borland
manuals.) Strcat() combines two strings into one.
Strcpy might come as a surprise to BASIC and PASCAL
programmers. In C, because a string is not a special data
type, but simply an array of characters, some things you
might take for granted will not work. For instance, you
cannot assign an variable number of characters to an array
which might be a different size simply with an = sign. C does
provide the library function strcpy() for that however.
Here's another useful program:
/* program #14 */
#include <magic.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void main(){
float millimeters;
char tempstr[80];
magic_setup();
xclear();
do {
pile("Enter a number of inches");
pile("or press [Esc] when done");
dialog(-1,3);
if (u != 27) {
millimeters = atoi(answer) * 25.4;
sprintf(answer,"%f",millimeters);
strcpy(tempstr,"That would be ");
strcat(tempstr,answer);
strcat(tempstr," millimeters.");
pile(tempstr);
getanykey(-1,16);
} /* end of if block */
} /* end of do block */
while (u != 27);
}
You'll notice in the seventh line that were are declaring
a variable of type "float." This means floating point and
must be used because the result of multiplication by 25.4 is
not usually going to be an integer. But this program looks a
bit messy. What are all those digits doing in the answer?
Lets fix it to truncate the answer to an integer:
/* program #15 */
#include <magic.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void main(){
float millimeters;
char tempstr[80];
magic_setup();
xclear();
do {
pile("Enter a number of inches");
pile("or press [Esc] when done");
dialog(-1,3);
if (u != 27) {
millimeters = atof(answer) * 25.4;
sprintf(answer,"%0.0f",millimeters);
strcpy(tempstr,"That would be ");
strcat(tempstr,answer);
strcat(tempstr," millimeters.");
pile(tempstr);
getanykey(-1,16);
} /* end of if block */
} /* end of do block*/
while (u != 27);
}
The trick is in the sprintf() line. This now has a width
and precision specifier, and the program now truncates the
answer to show an integer answer. There are several
variations available in outputting strings representing
numbers ranging from scientific notation to specific numbers
of leading and trailing zeroes or significant digits. For
more information, see your Borland Library Reference book.
Look up printf() and sprintf().
Program 16 converts Dupers from the imaginary country
Xanopieland to United States Dollars.
/* program #16 */
#include <magic.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#define CONVERSION 0.7143
void main(){
float dollars;
char tempstr[20];
magic_setup();
xclear();
centerjustify = 1;
strcpy(sent[1],"How many Dupers?");
dialog(-1,-1);
dollars = atof(answer) * CONVERSION;
sprintf(tempstr,"%0.2f",dollars);
strcat(sent[3],"$");
strcat(sent[3],tempstr);
strcpy(sent[1],"You own this much Xanopieland money:");
/* notice that sent[3] is assigned above sent[1] */
strcpy(sent[5],"Press any key to end program...");
getanykey(-1,-1);
}
This program shows several new features. In the sixth
line is "#define" which simply allows the programmer to type
the CONVERSION anywhere in the program and the compiler will
take it to mean 0.7143. This is very useful in large
programs. Instead of trying to remember 0.7143 every time
you need it, you can simply type CONVERSION in your source
code. Also, let's say you revise the program next year when
the conversion ratio advances to 0.7224. You simply change
your code in one place, but the proper conversion occurs
throughout your program. And, of course, it can be easier to
understand complicated code when you have used clear meanings
in your #defines.
Look at the "How many Dupers?" line. Notice that instead
of pile(), we used strcpy() and the variable "sent[1]." As
you may remember, "sent" is a two dimensional array which is
declared in the file MAGIC.H as "sent[46][81]". This allows
you to make a pile() of up to 46 separate sent(ences) up to
80 characters long each. (C assigns an ASCII #0 to the end
of character arrays, meaning you need room for one extra,
hence the [81].)
Pile() is nice because you don't have to keep track of
which sent[] you are using, but sometimes manual control is
useful.
The sprintf() line uses a precision specifier to cause
the output of the dollar amount to show two positions to the
right of the decimal point. Nothing else would look sensible
when converting to dollars and cents. Who ever heard of
$12.3, or $12.3700000?
And finally, you will see that the sent[]s are built up
out of order. And why not? This could be easier for you,
the programmer, but the final result will look the same.
This is something you could not do if you used pile().
Also, you'll notice that sent[2] and sent[4] are never
affected. They started out as blank lines, and in this
program we'll leave them that way.
Let's make one more improvement. In program #17, we'll
straighten out the way Dupers can be entered. The standard
dialog box is as wide as the widest sent[] and therefore the
answer can be that long. What if you want to limit the user
to a 4-digit answer, for instance? The following technique
also works well for use with filling in database forms:
/* program #17 */
#include <magic.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#define CONVERSION 0.7143
void main(){
float dollars;
char tempstr[20];
magic_setup();
xclear();
centerjustify = 1;
strcpy(sent[1],"How many Dupers?");
strcpy(sent[2]," ");
pop(-1,-1);
textcolor(boxtext);
textbackground(boxback);
xreadln(38,12,4);
restore();
dollars = atof(answer) * CONVERSION;
sprintf(tempstr,"%0.2f",dollars);
strcat(sent[3],"$");
strcat(sent[3],tempstr);
strcpy(sent[1],"You own this much Xanopieland money:");
strcpy(sent[5],"Press any key to end program...");
getanykey(-1,-1);
}
Can you spot the differences? This program does not
depend on dialog(). Instead, we pop up a box with "pop(),"
which is a simple function that remembers what's on the
screen, then puts a box on the screen. Since pop() will use
only as many sent[]s as have text, sent[2] is " ", or one
blank, so it will be included in pop()'s box. This will give
the user a line within the box on which to type the answer.
Then the current text color and background color are set
to the same as the colors within the box, so that xreadln(),
will work in the proper colors. Xreadln() is like the gets()
function, or the Turbo Pascal readln procedure, but with
three differences. Xreadln() accepts mouse control (left
mouse button is like pressing [Enter] and right button is
like [Esc]), Xreadln() requires two integers to designate the
screen coordinates at which it will echo the text typed by
the user, and a third integer which limits how long a string
the user is allowed to type.
Dialog() knows when the user is done, and restores the
screen and disappears, but pop() doesn't know what is wanted,
so stays on the screen until a specific call to restore(),
which wipes it out and replaces the previous screen image.
Near the top of the program you may also note we've added
"#include <conio.h>, the header file where names for colors
are defined. (With older Turbo C compilers, replace CONIO.H
with GRAPHICS.H.)
Program #18 shows many new techniques. It is the
Universal Money Converter. For simplicity, only the first
menu entry has a a function installed. Run this program to
see what it does, then study the source code to see how it
does it. From a practical point of view, you can see that
with modifications, this could be used for real money from
real countries. For that matter, the same program could be
modified for many purposes. How about a universal metric
converter that converts lengths, weights, temperatures, etc?
(NOTE: When I first roughed out this tutorial, I got to
thinking about the previous sentence, and an idea was born.
It is now complete and being distributed as shareware.
Perhaps you've seen it, THE UNIVERSAL CONVERTER. This new
program performs over 600 useful calculations and it makes
extensive use of the Magic library!)
/* program #18 */
#include <magic.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#define CONVERSION 0.7143
void xano(void); /*** 1 ***/
float dollars;
char tempstr[20];
void main(){
magic_setup();
xclear();
centerjustify = 1;
pile("Universal Money Converter");
pop(-1,2);
clearsents(); /*** 2 ***/
mc = 1; /*** 3 ***/
do {
pile("Xanopieland");
pile("Braze Island");
pile("Nomania");
pile("Grazille");
pile("Exit to Dos");
menu(-1,-1); /*** 4 ***/
if (u == 13) {
if (mc == 1) xano(); /*** 5 ***/
/* if (mc == 2) braze();
if (mc == 3) nomania();
if (mc == 4) Grazille(); *** 6 *** */
}
}
while (!((u == 13) && (mc == 5))); /*** 7 ***/
cleanup(); /*** 8 ***/
}
void xano(void){ /*** 9 ***/
pile("How many Xanopieland Dupers?");
pile(" "); //must be at least one char *** 10 ***
pop(-1,-1);
textcolor(boxtext);
textbackground(boxback);
xreadln(38,12,4);
restore();
dollars = atof(answer) * CONVERSION;
sprintf(tempstr,"%0.2f",dollars);
strcat(sent[3],"$");
strcat(sent[3],tempstr);
strcpy(sent[1],"You own this much Xanopieland money:");
getanykey(-1,-1);
} // end of xano function
Well, I've got some explaining to do this time. I've
marked the 10 changes in this program with comments so you
can follow all this:
The most noticeable change is the use of a sub-routine.
For the first time you define your own function (other than
main()). At Note One, we have the "declaration" of the
xano() function. This prototype merely tells the compiler
to expect the function and to know what information might be
passed to/from it. In this case, nothing is passed to it, so
the parentheses contain the word "void". Also, nothing will
come from it, so it is also preceded by the word "void." By
now, you've probably looked at a couple of header (.H) files
and recognize function declarations. This one could also
have been put in a header file, but there is no need, because
there are no other parts of the program in other files that
are going to use it.
Later, in the body of the program the xano() function is
"defined." So, to review, first we "declare" a function,
later we "define" it.
At Note 2, we've popped a box onto the screen. Remember,
that pop() is less sophisticated than other functions such as
getanykey() or dialog() which use it. These also clean
themselves off the screen when they are done. Pop() does
not. It leaves itself on the screen and leaves the sent[]s
that were used, full of text. Clearsents() is a Magic
function which restores all the sent[]s to empty strings so
you can use them again without having old text pop up in your
new boxes.
We're about to use a lightbar menu. But before we do, at
Note 3, we ought to decide which menu item will be
highlighted when the menu first appears.
At Note 4, we actually call the menu() function. It
starts like pop(), but lights up the sent[] corresponding to
the number "mc." Using the mouse or arrow keys, the user can
select an item then press [Enter] or [Esc]. When that
happens, the menu is done, and erases itself from the screen,
leaving behind a value in "mc" related to the menu item which
was highlighted when the user was done.
And at Note 5, we have a series of "if"s. In this case,
most of them are commented out, because we haven't actually
built the whole program. But, if the user quits the menu()
with 1 highlighted, then we do have function - xano().
Notice that we use two equal signs when checking for
equality. This is different than assigning a value to a
variable with one equal sign. If we were to do this:
if (mc = 2)
mc would become 2, but with == we harmlessly check it.
As I said, the other options are commented out (Note 6),
but if the user selects menu item 1, then control of the
program is given to the function xano().
Skipping ahead to Note #9: It doesn't matter where in
the source code xano() is defined, as long as it has been
declared in advance. Some programmers will put some
functions ahead of the main() function, and others will put
it below. This seems unusual to Pascal programmers who must
always list sub-routines above routines which call them.
At Note 9, you'll see a new kind of comment. This is
unique to C++, and will not work in standard C source code.
If you have an older C compiler, change these to the normal
kind of comments with /* and */. When the marker // occurs
on a line, everything to the right of that marker will be
ignored by the compiler.
Now, back up to Note 7. What a weird statement! What
we're doing here is repeating the whole menu sequence until
the user has highlighted menu item 5 ("Exit to Dos") AND
pressed [Enter]. C programmers do things like this - with
cryptic uses of the ! (not) operator and nested parentheses.
The best way to unravel a thing like this is to read it in
the same sequence as the compiler. Expressions in the
innermost parentheses are evaluated first. If you still find
it hard to read, sometimes you can put extra spaces on the
line to break the expression into bite-size groups, or even
cut it up into several lines.
And finally, at Note 8, we have a new function,
cleanup(), which, much like it sounds, cleans everything up,
freeing memory, clearing the screen and a few other chores.
When cleanup() is done, it stops the program and returns the
user to DOS.
Program #19 adds a switch statement. This cleans up the
multiple "if" statements, but has little overall effect on
the program, other than simplicity in the source code.
Notice the use of "break" after all but the last comparison
in the switch. This allows us to exit the switch once a
match has been found.
Then we've added another switch statement, allowing the
end user to press any key corresponding to the first letter
of a menu item, thereby executing the menu choice with a
single keystroke.
/* program #19 */
#include <magic.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <ctype.h>
#define CONVERSION 0.7143
void xano(void);
float dollars;
char tempstr[20];
void main(){
magic_setup();
xclear();
centerjustify = 1;
pile("Universal Money Converter");
pop(25,2);
clearsents();
mc = 1;
do {
pile("Xanopieland");
pile("Braze Island");
pile("Nomania");
pile("Grazille");
pile("Exit to Dos");
menu(-1,-1);
switch (toupper(u)) {
case 'X' : xano(); u = 126; break;
// case 'B' : braze(); u = 126; break;
// case 'N' : nomania(); u = 126; break;
// case 'G' : Grazille(); u = 126; break;
case 'E' : mc = 5; u = 13;
}
if (u == 13) switch (mc) {
case 1 : xano(); break;
// case 2 : braze(); break;
// case 3 : nomania(); break;
// case 4 : Grazille();
}
}
while (!((u == 13) && (mc == 5)));
cleanup();
}
void xano(void){
pile("How many Xanopieland Dupers?");
pile(" "); //must be at least one char
pop(-1,-1);
textcolor(boxtext);
textbackground(boxback);
xreadln(38,12,4);
restore();
dollars = atof(answer) * CONVERSION;
sprintf(tempstr,"%0.2f",dollars);
strcat(sent[3],"$");
strcat(sent[3],tempstr);
strcpy(sent[1],"You own this much Xanopieland money:");
getanykey(-1,-1);
} // end of xano function
You see, the whole block which calls pile() several
times, then menu(), then asseses the user input, is repeated,
because if the user presses any key except an arrow key, the
menu considers itself done and disappears. As the program
falls through, if the key the user pressed is not one
examined by the switch statement, or [Enter], then the menu
simply reappears, over and over again, until the user presses
[Enter] or the first letter of something on the menu.
Notice the new way of commenting out the non-existent
functions.
There are opportunities for bugs here. You want to make
sure u == 13 (the [Enter] key) before allowing the first
"switch" to execute, because otherwise, a function may run
twice in a row if the user presses a character key, first
when mc = whatever, then then when u = 'whatever'. Also make
sure to put the second case characters in single quote marks.
And last, notice the "toupper(u)." Without this the user
would have to press the shift key to get the desired result.
Toupper() converts whatever is in the variable u into a
capital letter.
THIS TUTORIAL IS CONTINUED IN THE FILE: TUTOR.2
(This file is split into two parts in case you are using an
older Turbo C compiler, which does not support files larger
than 64k.)